package com.derbysoft.synxisadapter.webservice.impl;

import com.derbysoft.common.util.concurrent.ExecutorUtils;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTACancelRQ;
import com.derbysoft.synxisadapter.commons.dto.synxis.OtaRequestMessage;
import com.derbysoft.synxisadapter.commons.dto.synxis.OtaResponseMessage;
import com.derbysoft.synxisadapter.commons.model.Channel;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.xml.transform.StringSource;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
 * User: jason
 * Date: 2012-11-28
 */
public abstract class AbstractSynXisWebService<RQ extends OtaRequestMessage, RS extends OtaResponseMessage> {

    protected static final String HEADER_TEMPLATE = "<HTNGHeader xmlns=\"http://htng.org/1.1/Header/\"><From><Credential>" +
            "<userName>${userName}</userName><password>${password}</password></Credential></From></HTNGHeader>";

    @Autowired
    @Qualifier("taskExecutor")
    private Executor executor;

    private static Map<String, String> headerMap = new ConcurrentHashMap<String, String>();

    private static final String CANCEL_ACTION = "http://synxis.com/OTA2004AService/CancelReservations";

    @SuppressWarnings("unchecked")
    protected RS invoke(WebServiceTemplate webServiceTemplate, final RQ rq, final Channel channel) throws Exception {
        return (RS) webServiceTemplate.marshalSendAndReceive(rq, new WebServiceMessageCallback() {
            @Override
            public void doWithMessage(WebServiceMessage webServiceMessage) throws IOException, TransformerException {
                SoapMessage soapMessage = (SoapMessage) webServiceMessage;
                SoapHeader soapHeader = soapMessage.getSoapHeader();
                if (rq instanceof OTACancelRQ) {
                    soapMessage.setSoapAction(CANCEL_ACTION);
                }
                Transformer transformer = TransformerFactory.newInstance().newTransformer();
                transformer.transform(new StringSource(createHeaderString(channel)), soapHeader.getResult());
            }
        });
    }

    public List<ResponsePair<RQ, RS>> doBatchInvoke(WebServiceTemplate webServiceTemplate, List<RQ> requests, Channel channel) throws Exception {
        Collection<Callable<ResponsePair<RQ, RS>>> taskPairs = new ArrayList<Callable<ResponsePair<RQ, RS>>>();
        for (RQ request : requests) {
            taskPairs.add(createTask(webServiceTemplate, request, channel));
        }
        return ExecutorUtils.batchExecute(executor, taskPairs);
    }

    @SuppressWarnings("unchecked")
    private Callable<ResponsePair<RQ, RS>> createTask(final WebServiceTemplate webServiceTemplate, final RQ rq, final Channel channel) throws Exception {
        return new Callable<ResponsePair<RQ, RS>>() {
            @Override
            public ResponsePair<RQ, RS> call() throws Exception {
                return new ResponsePair<RQ, RS>(rq, invoke(webServiceTemplate, rq, channel));
            }
        };
    }

    private String createHeaderString(Channel channel) {
        String key = makeHeaderKey(channel);
        if (headerMap.containsKey(key)) {
            return headerMap.get(key);
        }

        String result = HEADER_TEMPLATE;

        result = StringUtils.replace(result, "${userName}", channel.getUserName());
        result = StringUtils.replace(result, "${password}", channel.getPassword());

        headerMap.put(key, result);

        return result;
    }

    private String makeHeaderKey(Channel channel) {
        return channel.getUserName() + channel.getPassword();
    }
}
